home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1995 May / cd Ware (Juegos) Epimundo.iso / DOS / PRGMMING / PCXKIT51.ZIP / PCX.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-04  |  16.7 KB  |  543 lines

  1. /* Developed with Turbo (Borland) C++ for DOS, version 3.0.
  2.    Will compile only as COMPACT, LARGE, or HUGE, and the model
  3.    must match the .MODEL directive in the ASM files. */
  4.  
  5. // #define registered_version
  6.  
  7. /* ----------------------------------------------------------------------
  8.  
  9.                   PCX.CPP
  10.                                 Version 1.1
  11.  
  12.                  Copyright (c) 1994
  13.                  by Peter Donnelly
  14.                   Skookum Software
  15.                   1301 Ryan Street
  16.              Victoria BC Canada V8T 4Y8
  17.  
  18.    ╒══════════════════════════════════════════════════════════════════════╕
  19.    │  Permission is granted for the non-commercial distribution and       │
  20.    │  private use of this source code. This is shareware; if you use all  │
  21.    │  or portions of it in programs you distribute, or make any other     │
  22.    │  public use of it, you are expected to pay a modest registration     │
  23.    │  fee. Registered users will receive the latest version of the code,  │
  24.    │  including support for 256-color Super-VGA modes. Please see the     │
  25.    │  READ.ME file for details.                                           │
  26.    ╘══════════════════════════════════════════════════════════════════════╛
  27. */
  28. #include "pcx.h"
  29. #include <stdio.h>
  30. #include <conio.h>
  31. #include <io.h>
  32. #include <string.h>
  33. #include <dos.h>
  34. #include <alloc.h>
  35.  
  36. #define VIDEO 0x10
  37. #ifdef __cplusplus                     // don't mangle function names
  38.   extern "C" {
  39. #endif
  40. void Decode16(void);
  41. void Decode256(void);
  42. void DecodeSVGA256(void);
  43. #ifdef __cplusplus
  44. }
  45. #endif
  46.  
  47. // Assembler variables
  48.  
  49. int file_error;
  50. unsigned int ColumnCount;
  51. unsigned int Plane;
  52. unsigned int BytesPerLine;
  53. unsigned char RepeatCount;
  54. unsigned int DataLength;
  55. int LineEnd, ScreenWidth;
  56. int Margin;
  57. unsigned int WindowEnd, WindowStep, WindowPos;
  58. int BytesPerScanLine;
  59. int StartCol, XMax;
  60. int far *LineBuf;
  61. unsigned int LineBufSeg, LineBufOffs, LineBufIndex;
  62. int far *Scratch;              // needs to be normalized
  63. unsigned int VideoSeg, VideoOffs;
  64. char WriteWindow;
  65. unsigned int buffer_size;
  66. const num_modes = 11;
  67. union REGS inregs, outregs;
  68. struct SREGS segregs;
  69. FILE *pcx_file;
  70. pathstr pcx_filename;
  71. char *error_str;
  72. int pic_width;
  73. int our_modes[num_modes] = {0x0D, 0x0E, 0x10, 0x12, 0x13, 0x100, 0x101,
  74.                 0x102, 0x103, 0x105, 0x107};
  75.  
  76.  
  77. /* ------------------------ Structures ---------------------------- */
  78.  
  79. rgb_struct rgb_pal[15];
  80. pcx_header_struct pcx_header;
  81. VESA_info_struct VESA_info;
  82. mode_info_struct mode_info;
  83.  
  84. typedef rgb_struct cregisters[256];
  85. cregisters rgb256;
  86.  
  87. /* ---------------------- Video mode functions ------------------------- */
  88.  
  89. void video_off(BOOLEAN vid_status)
  90. // Hides the image by turning off video refresh. See Ferraro p. 468.
  91. {
  92. inregs.h.ah = 0x12;
  93. inregs.h.bl = 0x36;
  94. inregs.h.al = vid_status;
  95. int86(VIDEO, &inregs, &outregs);
  96. }
  97.  
  98.  
  99. int detect_VESA(VESA_info_struct *VESA_rec) // returns !0 if VESA BIOS present
  100. {
  101.   inregs.x.ax = 0x4F00;
  102.   segregs.es = FP_SEG(VESA_rec);
  103.   inregs.x.di = FP_OFF(VESA_rec);
  104.   int86x(VIDEO, &inregs, &outregs, &segregs);
  105.   if (outregs.h.ah) return 0;
  106.   return(!strncmp(VESA_rec->signature, "VESA", 4));
  107. }
  108.  
  109.  
  110. int detect_VGA(void)        // see Ferraro p. 887
  111. {
  112. inregs.h.ah = 0x1A;
  113. inregs.h.al = 0;
  114. int86(VIDEO, &inregs, &outregs);
  115. return(outregs.h.al != 0x1A);
  116. }
  117.  
  118.  
  119. int hardware_supports(unsigned int mode)    // returns 1 if mode supported
  120. {
  121.   unsigned int far *modeptr;
  122.  
  123.   if (mode >= 0x100) {
  124.     if (detect_VESA(&VESA_info)) {          // fills VESA_info structure
  125.       modeptr = VESA_info.mode_list_ptr;
  126.       do {
  127.     if (*modeptr++ == mode) return 1;
  128.       } while (*modeptr != 0xFFFF);
  129.     }
  130.     return 0;               // didn't find match, or no VESA
  131.   }
  132.   return 1;                 // not VESA mode; assume supported
  133. }
  134.  
  135. int we_support(unsigned int mode)           // returns 1 if mode supported
  136. {
  137.   int x;
  138.   int in_there = 0;
  139.  
  140.   for (x = 0; x < num_modes; x++) {
  141.     if (mode == our_modes[x]) in_there = 1; }
  142.   return in_there;
  143. }
  144.  
  145. int fits(pcx_header_struct header)
  146. {
  147.   return((header.xmax < header.hres) && (header.ymax < header.vres));
  148. }
  149.  
  150. void try_it(unsigned int mode, unsigned int *m)
  151. {
  152.   if ((hardware_supports(mode)) && (we_support(mode)))
  153.     *m = mode;
  154.   return;
  155. }
  156.  
  157. unsigned int best_mode(pcx_header_struct header)
  158. /* Finds the lowest resolution at which the picture will fit on screen
  159.    but not lower than the originating device. */
  160. {
  161.   unsigned int m;
  162.  
  163.   if (header.num_planes == 1) {
  164.     m = 0x13;
  165.     if ((header.hres > 320) || (!fits(header))) try_it(0x101, &m);
  166.     if ((header.hres > 640) || (!fits(header))) try_it(0x103, &m);
  167.     if ((header.hres > 800) || (!fits(header))) try_it(0x105, &m);
  168.     if ((header.hres > 1024) || (!fits(header))) try_it(0x107, &m);
  169.   } else if (header.num_planes == 4) {
  170.     m = (header.hres <= 320) ? 0x0D : 0x0E;
  171.     if ((header.vres > 200) || (!fits(header))) try_it(0x10, &m);
  172.     if ((header.vres > 350) || (!fits(header))) try_it(0x12, &m);
  173.     if ((header.vres > 480) || (!fits(header))) try_it(0x102, &m);
  174.   } else {
  175.     file_error = 5;
  176.     m = 0xFFFF;
  177.   }
  178.   return m;
  179.   }
  180.  
  181. void get_mode_info(unsigned int mode, mode_info_struct *m)
  182. // puts info on any VESA mode into the structure.
  183. {
  184.   inregs.x.ax = 0x4F01;           // VESA function
  185.   inregs.x.cx = mode;
  186.   segregs.es = FP_SEG(m);
  187.   inregs.x.di = FP_OFF(m);
  188.   int86x(VIDEO, &inregs, &outregs, &segregs);
  189.   /* Early versions of VESA BIOS extensions do not return values in the
  190.      xres and yres fields. We need to know yres for centering images. */
  191.   switch(mode) {
  192.     case 0x100: m->yres = 400; break;
  193.     case 0x101: m->yres = 480; break;
  194.     case 0x102: m->yres = 600; break;
  195.     case 0x103: m->yres = 600; break;
  196.     case 0x105: m->yres = 768; break;
  197.     case 0x107: m->yres = 1024;
  198.     }
  199.   return;
  200. }
  201.  
  202. int get_mode(void)
  203. {
  204.   int curr_mode;
  205.  
  206.   if (detect_VESA(&VESA_info)) {
  207.     inregs.x.ax = 0x4F03;
  208.     int86(VIDEO, &inregs, &outregs);
  209.     curr_mode = outregs.x.bx;          // may be inaccurate if not SVGA
  210.     curr_mode &= 0x3FFF;                //  - see Wilton p. 448
  211.     if (hardware_supports(curr_mode) && (curr_mode >= 0x100))
  212.       return curr_mode;
  213.   }
  214.     inregs.h.ah = 0x0F;                // return VGA mode
  215.     int86(VIDEO, &inregs, &outregs);
  216.     return outregs.h.al;
  217. }
  218.  
  219. void set_mode(int mode, int options)
  220. {
  221.   if (mode >= 0x100) {
  222.     if (options & save_mem) mode |= 0x8000;
  223.                   // set bit 15 to preserve video mem.
  224.     inregs.x.ax = 0x4F02;
  225.     inregs.x.bx = mode;
  226.     } else {
  227.     if (options & save_mem) mode |= 0x80;
  228.                   // set bit 7 to preserve video mem.
  229.     inregs.x.ax = mode;           // only use low byte
  230.     inregs.h.ah = 0;              // function no.
  231.     }
  232.     int86(VIDEO, &inregs, &outregs);
  233.   return;
  234. }
  235.  
  236. void put_window(int step, char window)
  237. {
  238.   inregs.x.ax = 0x4f05;           // VESA window function
  239.   inregs.h.bh = 0;                 // move-window subfunction
  240.   inregs.h.bl = window;                // window A    NEED TO CHECK WRITABILITY!!
  241.   inregs.x.dx = step;             // window position, in granules
  242.   int86(VIDEO, &inregs, &outregs);
  243. }
  244.  
  245. /* ------------------------ Palette functions ------------------------- */
  246.  
  247. void set_color_registers(cregisters rgb)
  248. {
  249. // Replaces the BGI setrgbpalette function
  250.  
  251.   inregs.h.ah = 0x10;             // function
  252.   inregs.h.al = 0x12;             // subfunction
  253.   segregs.es = FP_SEG(rgb);       // list of colors to put in
  254.   inregs.x.dx = FP_OFF(rgb);
  255.   inregs.x.bx = 0;                // first register to change
  256.   inregs.x.cx = 0x100;            // how many regs to change
  257.   int86x(VIDEO, &inregs, &outregs, &segregs);
  258.   return;
  259. }
  260.  
  261. void set_palette(unsigned char *palette)
  262. {
  263. // Replaces the BGI setallpalette function
  264.  
  265.   inregs.h.ah = 0x10;             // function
  266.   inregs.h.al = 0x02;             // subfunction
  267.   segregs.es = FP_SEG(palette);   // list of 17 values to put in
  268.   inregs.x.dx = FP_OFF(palette);
  269.   int86x(VIDEO, &inregs, &outregs, &segregs);
  270.   return;
  271. }
  272.  
  273. long get_256_palette(FILE *the_file, cregisters rgb256)
  274.  
  275. /* File must be open. Returns palette offset if present, 0 if not.
  276.  
  277.    The last 769 bytes of the file are palette information, starting with
  278.    a one-byte identifier. Each group of three bytes represents the RGB
  279.    values of one of the color registers. We take the 6 most significant
  280.    bits to bring the values within the range 0-63 expected by the registers. */
  281. {
  282.   unsigned char palette_flag;
  283.   long palette_start;
  284.   int x;
  285.  
  286.   palette_start = filelength(fileno(the_file)) - 769;
  287.   fseek(the_file, palette_start, SEEK_SET);
  288.   palette_flag = getc(the_file);
  289.   if ((palette_flag != 12) || (pcx_header.version < 5)
  290.      || (fread(rgb256, 3, 256, the_file) < 256)) {
  291.     file_error = 2;
  292.     return 0;
  293.   }
  294.   for (x = 0; x < 256; x++) {
  295.     rgb256[x].r >>= 2;
  296.     rgb256[x].g >>= 2;
  297.     rgb256[x].b >>= 2;
  298.   }
  299.   return palette_start;
  300. }
  301.  
  302. /* ---------------------- Miscellaneous functions ---------------------- */
  303.  
  304. FILE *open_file(pathstr pic_file_name, char *file_mode,
  305.         pcx_header_struct *header)
  306. {
  307.   FILE *f;
  308.  
  309.   f = fopen(pic_file_name, file_mode);
  310.   if (f != NULL) fread(header, 1, 128, f);
  311.   return f;
  312. }
  313.  
  314. char *report_error(int error)
  315. {
  316.   switch(error) {
  317.     case 1: return "Could not open file.\n";
  318.     case 2: return "No palette information in file.\n";
  319.     case 3: return "Picture is too wide for requested video mode.\n";
  320.     case 4: return "Number of colors in file does not match selected mode.\n";
  321.     case 5: return "Unsupported picture format.\n";
  322.     default: return "Undefined error.\n";
  323.     }
  324. }
  325.  
  326. void get_margin(int screenwd, int *margin, int *line_end,
  327.                 pcx_header_struct header)
  328. {
  329. // Calculates how many pixels have to be skipped when advancing to
  330. // the next line, so files of less than screen width can be displayed
  331. // and centered.
  332.  
  333.   *line_end = header.bytes_per_line;
  334.   *margin = screenwd - *line_end;
  335.   if (*margin < 0) file_error = 3;          // too wide
  336.   return;
  337. }
  338.  
  339. unsigned int set_buffer_size(void)
  340. {
  341.   unsigned int buf;
  342.  
  343.   buf = 64512;
  344.   if (buf > coreleft())
  345.     buf = coreleft() - (coreleft() % 1024);
  346.   // fread is faster if buffer_size is a round number?
  347.   return buf;
  348. }
  349.  
  350. long get_first_pix(pcx_header_struct header, int optns,
  351.                    int screenwid, int screenht)
  352.  
  353. /* The image is centered if the Options call for it. Otherwise it is offset
  354.   on the screen according to the values of XMin and YMin in the file header.
  355.   These are usually zero. This function returns the offset in bytes from
  356.   the start of the video buffer to where the first pixel will be written. */
  357. {
  358.  
  359.   long first_pix = 0;
  360.   int picwid, picht;
  361.  
  362.   picwid = header.xmax - header.xmin + 1;
  363.   if (header.bits_per_plane == 1) picwid /= 8;
  364.   picht = header.ymax - header.ymin;
  365.   if (picht < screenht) {
  366.     if (!(optns & vcenter))
  367.       first_pix += (long)header.ymin * screenwid;
  368.     else first_pix += (long)(screenht-1-picht) / 2 * screenwid;
  369.   };
  370.   if (picwid < screenwid) {
  371.     if (!(optns & hcenter)) first_pix += header.xmin;
  372.     else first_pix += (screenwid - picwid) / 2;
  373.   };
  374. return first_pix;
  375. }
  376.  
  377.  
  378. /* ---------------- Main procedure for 16-color modes ------------------ */
  379.  
  380. int read_16(FILE *pic_file, unsigned int mode, unsigned int options)
  381. {
  382.      // don't call directly; needs pcx_header initialized by read_it()
  383.   typedef unsigned char palette_bytes[3];
  384.  
  385.   unsigned char  entry, gun, pcx_code;
  386.   unsigned char pal_regs[17];
  387.   unsigned int screen_height;
  388.  
  389.   if (pcx_header.num_planes != 4) return 4;
  390.   if (mode >= 0x100) {
  391.     get_mode_info(mode, &mode_info);
  392.     ScreenWidth = mode_info.bytes_per_line;
  393.     screen_height = mode_info.yres;
  394.   } else
  395.   switch(mode) {
  396.     case 0x0D: ScreenWidth = 40; screen_height = 200; break;
  397.     case 0x0E: ScreenWidth = 80; screen_height = 200; break;
  398.     case 0x10: ScreenWidth = 80; screen_height = 350; break;
  399.     case 0x12: ScreenWidth = 80; screen_height = 480; break;
  400.   }
  401.   get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
  402.   if (file_error) return file_error;
  403.   VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, screen_height);
  404.   VideoSeg = 0xA000;              // Segment of video memory
  405.   outportb(0x3C4, 2);             // Index to map mask register }
  406.   Plane = 1;                      // Initialize plane }
  407.   outportb(0x3C5, Plane);         // Set sequencer to mask out other planes }
  408.  
  409.   // --- Decipher 16-color palette
  410.  
  411.   /*  The palette information is stored in bytes 16-63 of the header. Each of
  412.      the 16 palette slots is allotted 3 bytes - one for each primary color.
  413.      Any of these bytes can have a value of 0-255. However, the VGA is
  414.      capable only of 6-bit RGB values (making for 64x64x64 = 256K possible
  415.      colors), so we take only the 6 most significant bits from each PCX
  416.      color value.
  417.  
  418.      In 16-color modes, the VGA uses the 16 CGA/EGA palette registers.
  419.      However, the actual color values (18 bits per slot) won't fit here,
  420.      so the palette registers are used as pointers to 16 of the 256 color
  421.      registers, which hold the RGB values.
  422.  
  423.      What we have to do is extract the RGB values from the PCX header, put
  424.      them in the first 16 color registers, then set the palette to point to
  425.      those registers. */
  426.  
  427.   for (entry = 0; entry < 16; entry++) {
  428.     for (gun = 0; gun < 3; gun++) {
  429.       pcx_code = ((palette_bytes &)pcx_header.palette[entry])[gun];
  430.       switch (gun) {
  431.     case 0: rgb_pal[entry].r = pcx_code >> 2; break;
  432.     case 1: rgb_pal[entry].g = pcx_code >> 2; break;
  433.     case 2: rgb_pal[entry].b = pcx_code >> 2;
  434.       }
  435.     }
  436.     pal_regs[entry] = entry;
  437.   }
  438.   pal_regs[16] = 0;               // overscan color
  439.   set_color_registers(rgb_pal);   // RGB values into registers 0-15
  440.   set_palette(pal_regs);          // point to registers 0-15
  441.  
  442.   // --- Read and decode the image data ---
  443.  
  444.   BytesPerLine = pcx_header.bytes_per_line;
  445.   RepeatCount = 0;                // Initialize assembler vars.
  446.   ColumnCount = 0;
  447.   buffer_size = set_buffer_size();
  448.   Scratch = (int far*)malloc(buffer_size);
  449.   fseek(pic_file, 128, SEEK_SET);
  450.   do {
  451.     DataLength = fread(Scratch, 1, buffer_size, pic_file);
  452.     Decode16();
  453.   } while (!feof(pic_file));
  454.   free(Scratch);
  455.   outportb(0x3C5, 0xF);           // Reset mask map
  456.   return 0;
  457. }
  458.  
  459. /* -------------------------- VGA 256-color modes ---------------------- */
  460.  
  461. int read_256(FILE *pic_file, unsigned int mode, unsigned int options)
  462. {
  463.      // don't call directly; needs pcx_header initialized by read_it()
  464.   long palette_start, total_read;
  465.  
  466.   if (pcx_header.num_planes != 1) return 4;
  467.   palette_start = get_256_palette(pic_file, rgb256);
  468.   if (!palette_start) return 2;
  469.   ScreenWidth = 320;
  470.   get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
  471.   if (file_error) return file_error;
  472.   fseek(pic_file, 128, SEEK_SET);
  473.   total_read = 128;
  474.   RepeatCount = 0;
  475.   set_color_registers(rgb256);
  476.   VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, 200);
  477.   VideoSeg = 0xA000;
  478.   buffer_size = set_buffer_size();
  479.   Scratch = (int far*)malloc(buffer_size);
  480.   do {
  481.     DataLength = fread(Scratch, 1, buffer_size, pic_file);
  482.     total_read += DataLength;
  483.     if (total_read > palette_start)
  484.       DataLength -= (total_read - palette_start);
  485.     Decode256();
  486.   } while ((!feof(pic_file)) && (total_read < palette_start));
  487.   free(Scratch);
  488.   if (mode);                 // to stop compiler warning
  489.   return 0;
  490. }
  491.  
  492. /* ---------------------- SVGA 256-color files ------------------------- */
  493.  
  494. #ifdef registered_version
  495.   #include "svga256.h"
  496. #else
  497.  
  498. int read_SVGA256(FILE *pic_file, unsigned int mode, unsigned int options)
  499. {
  500.   set_mode(3, no_options);
  501.   printf("Support for this video mode is available only to registered\n"
  502.      "users of PCX.CPP. Please see READ.ME for details.\n");
  503.   return 0;
  504. }
  505. #endif
  506.  
  507. /* ---------------------------- read_it() ------------------------------ */
  508.  
  509. int read_it(pathstr pic_file_name, int mode, int options)
  510. {           // returns file_error
  511.   FILE *pcx_file;
  512.  
  513.   file_error = 0;
  514.   pcx_file = open_file(pic_file_name, "rb", &pcx_header);
  515.   if (pcx_file == NULL) return 1;
  516.   if ((pcx_header.bits_per_plane < 8) && (pcx_header.num_planes == 1)) {
  517.     fclose(pcx_file);
  518.     return 5;
  519.   }
  520.   if (mode == auto_set) mode = best_mode(pcx_header);
  521.   set_mode(mode, options);
  522.   if (options & blackout) video_off(true);
  523.   switch(mode) {
  524.     case 0x0D:
  525.     case 0x0E:
  526.     case 0x10:
  527.     case 0x12:
  528.     case 0x102: file_error = read_16(pcx_file, mode, options);
  529.      break;
  530.     case 0x13: file_error = read_256(pcx_file, mode, options);
  531.      break;
  532.     case 0x100:
  533.     case 0x101:
  534.     case 0x103:
  535.     case 0x105:
  536.     case 0x107: file_error = read_SVGA256(pcx_file, mode, options);
  537.      break;
  538.   }
  539.   if (options & blackout) video_off(false);
  540.   fclose(pcx_file);
  541.   return file_error;
  542. }
  543.